package com.demo.utils;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

public class DateUtils {

    // 常用日期格式
    public static final String DEFAULT_PATTERN = "yyyy-MM-dd HH:mm:ss";
    public static final String DATE_PATTERN = "yyyy-MM-dd";
    public static final String TIME_PATTERN = "HH:mm:ss";
    public static final String YEAR_MONTH_PATTERN = "yyyy-MM";
    public static final String MONTH_DAY_PATTERN = "MM-dd";
    public static final String TIMESTAMP_PATTERN = "yyyyMMddHHmmss";

    // 线程安全的格式化器
    private static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormatter.ofPattern(DEFAULT_PATTERN);
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DATE_PATTERN);
    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern(TIME_PATTERN);
    private static final DateTimeFormatter YEAR_MONTH_FORMATTER = DateTimeFormatter.ofPattern(YEAR_MONTH_PATTERN);
    private static final DateTimeFormatter MONTH_DAY_FORMATTER = DateTimeFormatter.ofPattern(MONTH_DAY_PATTERN);
    private static final DateTimeFormatter TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern(TIMESTAMP_PATTERN);

    // 默认时区，可通过setDefaultZone方法修改
    private static ZoneId defaultZone = ZoneId.systemDefault();

    /**
     * 获取当前时间的毫秒数
     *
     * @return 当前时间戳（毫秒）
     */
    public static long getCurrentTimeMillis() {
        return Instant.now().toEpochMilli();
    }

    /**
     * 获取当前日期时间
     *
     * @return 当前LocalDateTime
     */
    public static LocalDateTime now() {
        return LocalDateTime.now(defaultZone);
    }

    /**
     * 获取当前日期（不包含时间）
     *
     * @return 当前LocalDate
     */
    public static LocalDate today() {
        return LocalDate.now(defaultZone);
    }

    /**
     * 获取当前时间（不包含日期）
     *
     * @return 当前LocalTime
     */
    public static LocalTime currentTime() {
        return LocalTime.now(defaultZone);
    }

    /**
     * 将时间戳转换为格式化日期字符串（使用默认格式）
     *
     * @param timestamp 时间戳（毫秒）
     * @return 格式化后的日期字符串
     */
    public static String format(long timestamp) {
        return format(Instant.ofEpochMilli(timestamp), DEFAULT_FORMATTER);
    }

    /**
     * 将时间戳转换为格式化日期字符串
     *
     * @param timestamp 时间戳（毫秒）
     * @param pattern   日期时间格式，如 "yyyy-MM-dd HH:mm:ss"
     * @return 格式化后的日期字符串
     */
    public static String format(long timestamp, String pattern) {
        return format(Instant.ofEpochMilli(timestamp), pattern);
    }

    /**
     * 将Instant转换为格式化日期字符串
     *
     * @param instant  Instant对象
     * @param pattern  日期时间格式
     * @return 格式化后的日期字符串
     */
    public static String format(Instant instant, String pattern) {
        return format(instant, DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 将Instant转换为格式化日期字符串
     *
     * @param instant   Instant对象
     * @param formatter 日期格式化器
     * @return 格式化后的日期字符串
     */
    public static String format(Instant instant, DateTimeFormatter formatter) {
        return LocalDateTime.ofInstant(instant, defaultZone).format(formatter);
    }

    /**
     * 将LocalDateTime转换为格式化日期字符串（使用默认格式）
     *
     * @param dateTime LocalDateTime对象
     * @return 格式化后的日期字符串
     */
    public static String format(LocalDateTime dateTime) {
        return dateTime.format(DEFAULT_FORMATTER);
    }

    /**
     * 将LocalDateTime转换为格式化日期字符串
     *
     * @param dateTime LocalDateTime对象
     * @param pattern  日期时间格式
     * @return 格式化后的日期字符串
     */
    public static String format(LocalDateTime dateTime, String pattern) {
        return dateTime.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 将LocalDate转换为格式化日期字符串（使用默认日期格式）
     *
     * @param date LocalDate对象
     * @return 格式化后的日期字符串
     */
    public static String format(LocalDate date) {
        return date.format(DATE_FORMATTER);
    }

    /**
     * 将LocalDate转换为格式化日期字符串
     *
     * @param date    LocalDate对象
     * @param pattern 日期格式
     * @return 格式化后的日期字符串
     */
    public static String format(LocalDate date, String pattern) {
        return date.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 解析日期字符串为LocalDateTime
     *
     * @param dateStr 日期字符串
     * @param pattern 对应的格式
     * @return LocalDateTime对象
     */
    public static LocalDateTime parseLocalDateTime(String dateStr, String pattern) {
        return LocalDateTime.parse(dateStr, DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 解析日期字符串为LocalDateTime（使用默认格式）
     *
     * @param dateStr 日期字符串
     * @return LocalDateTime对象
     */
    public static LocalDateTime parseLocalDateTime(String dateStr) {
        return LocalDateTime.parse(dateStr, DEFAULT_FORMATTER);
    }

    /**
     * 解析日期字符串为LocalDate
     *
     * @param dateStr 日期字符串
     * @param pattern 对应的格式
     * @return LocalDate对象
     */
    public static LocalDate parseLocalDate(String dateStr, String pattern) {
        return LocalDate.parse(dateStr, DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 解析日期字符串为LocalDate（使用默认日期格式）
     *
     * @param dateStr 日期字符串
     * @return LocalDate对象
     */
    public static LocalDate parseLocalDate(String dateStr) {
        return LocalDate.parse(dateStr, DATE_FORMATTER);
    }

    /**
     * 解析日期字符串为时间戳
     *
     * @param dateStr 日期字符串
     * @param pattern 对应的格式
     * @return 时间戳（毫秒）
     */
    public static long parse(String dateStr, String pattern) {
        LocalDateTime dateTime = parseLocalDateTime(dateStr, pattern);
        return dateTime.atZone(defaultZone).toInstant().toEpochMilli();
    }

    /**
     * 将LocalDateTime转换为时间戳
     *
     * @param dateTime LocalDateTime对象
     * @return 时间戳（毫秒）
     */
    public static long toTimestamp(LocalDateTime dateTime) {
        return dateTime.atZone(defaultZone).toInstant().toEpochMilli();
    }

    /**
     * 将时间戳转换为LocalDateTime
     *
     * @param timestamp 时间戳（毫秒）
     * @return LocalDateTime对象
     */
    public static LocalDateTime toLocalDateTime(long timestamp) {
        return Instant.ofEpochMilli(timestamp).atZone(defaultZone).toLocalDateTime();
    }

    /**
     * 将Date转换为LocalDateTime
     *
     * @param date Date对象
     * @return LocalDateTime对象
     */
    public static LocalDateTime toLocalDateTime(Date date) {
        return date.toInstant().atZone(defaultZone).toLocalDateTime();
    }

    /**
     * 将LocalDateTime转换为Date
     *
     * @param dateTime LocalDateTime对象
     * @return Date对象
     */
    public static Date toDate(LocalDateTime dateTime) {
        return Date.from(dateTime.atZone(defaultZone).toInstant());
    }

    /**
     * 计算两个日期之间的天数差
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 天数差
     */
    public static long daysBetween(LocalDate startDate, LocalDate endDate) {
        return ChronoUnit.DAYS.between(startDate, endDate);
    }

    /**
     * 计算两个日期之间的天数差
     *
     * @param startDate 开始日期（毫秒）
     * @param endDate   结束日期（毫秒）
     * @return 天数差
     */
    public static long daysBetween(long startDate, long endDate) {
        LocalDate start = toLocalDateTime(startDate).toLocalDate();
        LocalDate end = toLocalDateTime(endDate).toLocalDate();
        return daysBetween(start, end);
    }

    /**
     * 计算两个日期之间的月数差
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 月数差
     */
    public static long monthsBetween(LocalDate startDate, LocalDate endDate) {
        return ChronoUnit.MONTHS.between(startDate, endDate);
    }

    /**
     * 计算两个日期之间的年数差
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 年数差
     */
    public static long yearsBetween(LocalDate startDate, LocalDate endDate) {
        return ChronoUnit.YEARS.between(startDate, endDate);
    }

    /**
     * 添加指定天数到给定日期上
     *
     * @param date  原始日期
     * @param days  要添加的天数
     * @return 新的日期
     */
    public static LocalDate addDays(LocalDate date, int days) {
        return date.plusDays(days);
    }

    /**
     * 添加指定天数到给定日期时间上
     *
     * @param dateTime 原始日期时间
     * @param days     要添加的天数
     * @return 新的日期时间
     */
    public static LocalDateTime addDays(LocalDateTime dateTime, int days) {
        return dateTime.plusDays(days);
    }

    /**
     * 添加指定月数到给定日期上
     *
     * @param date   原始日期
     * @param months 要添加的月数
     * @return 新的日期
     */
    public static LocalDate addMonths(LocalDate date, int months) {
        return date.plusMonths(months);
    }

    /**
     * 添加指定月数到给定日期时间上
     *
     * @param dateTime 原始日期时间
     * @param months   要添加的月数
     * @return 新的日期时间
     */
    public static LocalDateTime addMonths(LocalDateTime dateTime, int months) {
        return dateTime.plusMonths(months);
    }

    /**
     * 添加指定年数到给定日期上
     *
     * @param date  原始日期
     * @param years 要添加的年数
     * @return 新的日期
     */
    public static LocalDate addYears(LocalDate date, int years) {
        return date.plusYears(years);
    }

    /**
     * 添加指定年数到给定日期时间上
     *
     * @param dateTime 原始日期时间
     * @param years    要添加的年数
     * @return 新的日期时间
     */
    public static LocalDateTime addYears(LocalDateTime dateTime, int years) {
        return dateTime.plusYears(years);
    }

    /**
     * 添加指定小时到给定日期时间上
     *
     * @param dateTime 原始日期时间
     * @param hours    要添加的小时数
     * @return 新的日期时间
     */
    public static LocalDateTime addHours(LocalDateTime dateTime, int hours) {
        return dateTime.plusHours(hours);
    }

    /**
     * 添加指定分钟到给定日期时间上
     *
     * @param dateTime 原始日期时间
     * @param minutes  要添加的分钟数
     * @return 新的日期时间
     */
    public static LocalDateTime addMinutes(LocalDateTime dateTime, int minutes) {
        return dateTime.plusMinutes(minutes);
    }

    /**
     * 添加指定秒数到给定日期时间上
     *
     * @param dateTime 原始日期时间
     * @param seconds  要添加的秒数
     * @return 新的日期时间
     */
    public static LocalDateTime addSeconds(LocalDateTime dateTime, int seconds) {
        return dateTime.plusSeconds(seconds);
    }

    /**
     * 判断是否是同一天
     *
     * @param timestamp1 第一个时间戳
     * @param timestamp2 第二个时间戳
     * @return true 如果是同一天
     */
    public static boolean isSameDay(long timestamp1, long timestamp2) {
        LocalDate date1 = toLocalDateTime(timestamp1).toLocalDate();
        LocalDate date2 = toLocalDateTime(timestamp2).toLocalDate();
        return date1.isEqual(date2);
    }

    /**
     * 判断是否是同一月
     *
     * @param timestamp1 第一个时间戳
     * @param timestamp2 第二个时间戳
     * @return true 如果是同一月
     */
    public static boolean isSameMonth(long timestamp1, long timestamp2) {
        LocalDate date1 = toLocalDateTime(timestamp1).toLocalDate();
        LocalDate date2 = toLocalDateTime(timestamp2).toLocalDate();
        return date1.getYear() == date2.getYear() && date1.getMonth() == date2.getMonth();
    }

    /**
     * 判断是否是同一年
     *
     * @param timestamp1 第一个时间戳
     * @param timestamp2 第二个时间戳
     * @return true 如果是同一年
     */
    public static boolean isSameYear(long timestamp1, long timestamp2) {
        LocalDate date1 = toLocalDateTime(timestamp1).toLocalDate();
        LocalDate date2 = toLocalDateTime(timestamp2).toLocalDate();
        return date1.getYear() == date2.getYear();
    }

    /**
     * 获取本月第一天
     *
     * @return 本月第一天的LocalDate
     */
    public static LocalDate getFirstDayOfMonth() {
        return today().withDayOfMonth(1);
    }

    /**
     * 获取本月最后一天
     *
     * @return 本月最后一天的LocalDate
     */
    public static LocalDate getLastDayOfMonth() {
        return today().withDayOfMonth(today().lengthOfMonth());
    }

    /**
     * 获取指定日期所在月的第一天
     *
     * @param date 指定日期
     * @return 该月第一天的LocalDate
     */
    public static LocalDate getFirstDayOfMonth(LocalDate date) {
        return date.withDayOfMonth(1);
    }

    /**
     * 获取指定日期所在月的最后一天
     *
     * @param date 指定日期
     * @return 该月最后一天的LocalDate
     */
    public static LocalDate getLastDayOfMonth(LocalDate date) {
        return date.withDayOfMonth(date.lengthOfMonth());
    }

    /**
     * 获取本周第一天（周一）
     *
     * @return 本周第一天的LocalDate
     */
    public static LocalDate getFirstDayOfWeek() {
        return today().with(java.time.temporal.TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
    }

    /**
     * 获取本周最后一天（周日）
     *
     * @return 本周最后一天的LocalDate
     */
    public static LocalDate getLastDayOfWeek() {
        return today().with(java.time.temporal.TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));
    }

    /**
     * 设置默认时区
     *
     * @param zoneId 时区ID
     */
    public static void setDefaultZone(ZoneId zoneId) {
        defaultZone = zoneId;
    }

    /**
     * 设置默认时区
     *
     * @param timeZone 时区
     */
    public static void setDefaultZone(TimeZone timeZone) {
        defaultZone = timeZone.toZoneId();
    }

    /**
     * 获取默认时区
     *
     * @return 默认时区
     */
    public static ZoneId getDefaultZone() {
        return defaultZone;
    }

    /**
     * 判断是否是闰年
     *
     * @param year 年份
     * @return true 如果是闰年
     */
    public static boolean isLeapYear(int year) {
        return Year.isLeap(year);
    }

    /**
     * 获取指定日期是星期几（中文表示）
     *
     * @param date 指定日期
     * @return 星期几的中文表示
     */
    public static String getWeekdayChinese(LocalDate date) {
        DayOfWeek dayOfWeek = date.getDayOfWeek();
        switch (dayOfWeek) {
            case MONDAY:
                return "星期一";
            case TUESDAY:
                return "星期二";
            case WEDNESDAY:
                return "星期三";
            case THURSDAY:
                return "星期四";
            case FRIDAY:
                return "星期五";
            case SATURDAY:
                return "星期六";
            case SUNDAY:
                return "星期日";
            default:
                return "";
        }
    }

    /**
     * 获取指定日期是星期几（英文缩写）
     *
     * @param date 指定日期
     * @return 星期几的英文缩写
     */
    public static String getWeekdayAbbreviated(LocalDate date) {
        return date.getDayOfWeek().getDisplayName(java.time.format.TextStyle.SHORT, Locale.ENGLISH);
    }

    /**
     * 获取当前时间的时间戳字符串（格式：yyyyMMddHHmmss）
     *
     * @return 当前时间的时间戳字符串
     */
    public static String getTimestampString() {
        return format(now(), TIMESTAMP_PATTERN);
    }

    /**
     * 判断一个日期是否在另一个日期之前
     *
     * @param date1 第一个日期
     * @param date2 第二个日期
     * @return true 如果date1在date2之前
     */
    public static boolean isBefore(LocalDate date1, LocalDate date2) {
        return date1.isBefore(date2);
    }

    /**
     * 判断一个日期时间是否在另一个日期时间之前
     *
     * @param dateTime1 第一个日期时间
     * @param dateTime2 第二个日期时间
     * @return true 如果dateTime1在dateTime2之前
     */
    public static boolean isBefore(LocalDateTime dateTime1, LocalDateTime dateTime2) {
        return dateTime1.isBefore(dateTime2);
    }

    /**
     * 判断一个日期是否在另一个日期之后
     *
     * @param date1 第一个日期
     * @param date2 第二个日期
     * @return true 如果date1在date2之后
     */
    public static boolean isAfter(LocalDate date1, LocalDate date2) {
        return date1.isAfter(date2);
    }

    /**
     * 判断一个日期时间是否在另一个日期时间之后
     *
     * @param dateTime1 第一个日期时间
     * @param dateTime2 第二个日期时间
     * @return true 如果dateTime1在dateTime2之后
     */
    public static boolean isAfter(LocalDateTime dateTime1, LocalDateTime dateTime2) {
        return dateTime1.isAfter(dateTime2);
    }

    /**
     * 判断两个日期是否相等
     *
     * @param date1 第一个日期
     * @param date2 第二个日期
     * @return true 如果两个日期相等
     */
    public static boolean isEqual(LocalDate date1, LocalDate date2) {
        return date1.isEqual(date2);
    }

    /**
     * 判断两个日期时间是否相等
     *
     * @param dateTime1 第一个日期时间
     * @param dateTime2 第二个日期时间
     * @return true 如果两个日期时间相等
     */
    public static boolean isEqual(LocalDateTime dateTime1, LocalDateTime dateTime2) {
        return dateTime1.isEqual(dateTime2);
    }

    /**
     * 获取一天的开始时间（00:00:00）
     *
     * @param date 指定日期
     * @return 该天开始时间的LocalDateTime
     */
    public static LocalDateTime getStartOfDay(LocalDate date) {
        return date.atStartOfDay();
    }

    /**
     * 获取一天的结束时间（23:59:59）
     *
     * @param date 指定日期
     * @return 该天结束时间的LocalDateTime
     */
    public static LocalDateTime getEndOfDay(LocalDate date) {
        return LocalDateTime.of(date, LocalTime.MAX);
    }

    /**
     * 获取年龄
     *
     * @param birthDate 出生日期
     * @return 年龄
     */
    public static int getAge(LocalDate birthDate) {
        return Period.between(birthDate, today()).getYears();
    }

    /**
     * 获取年龄
     *
     * @param birthDate 出生日期字符串（格式：yyyy-MM-dd）
     * @return 年龄
     */
    public static int getAge(String birthDate) {
        return getAge(parseLocalDate(birthDate));
    }
}